package com.wechat.common.web.handler;


import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.wechat.common.web.exception.BusinessException;
import com.wechat.common.web.result.Result;
import com.wechat.common.web.result.ResultCode;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.*;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 业务异常处理
     */
    @ExceptionHandler(BusinessException.class)
    public Result<?> businessExceptionHandler(BusinessException e) {
        log.warn("业务异常: code={}, message={}", e.getCode(), e.getMessage());
        return Result.fail(e.getCode(), e.getMessage());
    }

    /**
     * 参数校验异常处理 - @RequestBody 参数校验
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        Map<String, String> fieldErrors = new LinkedHashMap<>();
        StringBuilder errorMsg = new StringBuilder();
        // 收集所有字段错误
        ex.getBindingResult().getAllErrors().forEach(error -> {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                String fieldName = fieldError.getField();
                String errorMessage = fieldError.getDefaultMessage();
                fieldErrors.put(fieldName, errorMessage);

                if (errorMsg.length() > 0) {
                    errorMsg.append("; ");
                }
                errorMsg.append(fieldName).append(": ").append(errorMessage);
            } else {
                // 处理对象级别的错误
                String errorMessage = error.getDefaultMessage();
                if (errorMsg.length() > 0) {
                    errorMsg.append("; ");
                }
                errorMsg.append(errorMessage);
            }
        });
        log.warn("参数校验失败: {}", fieldErrors);
        // 返回第一个错误信息，或者可以返回所有错误信息
        String firstError = fieldErrors.values().iterator().hasNext()
                ? fieldErrors.values().iterator().next()
                : "参数校验失败";

        return Result.fail(ResultCode.PARAM_INVALID.getCode(), firstError);
    }

    /**
     * 参数校验异常处理 - 表单绑定异常
     */
    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException ex) {
        Map<String, String> fieldErrors = new LinkedHashMap<>();
        StringBuilder errorMsg = new StringBuilder();

        ex.getBindingResult().getAllErrors().forEach(error -> {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                String fieldName = fieldError.getField();
                String errorMessage = fieldError.getDefaultMessage();
                fieldErrors.put(fieldName, errorMessage);

                if (errorMsg.length() > 0) {
                    errorMsg.append("; ");
                }
                errorMsg.append(fieldName).append(": ").append(errorMessage);
            }
        });

        log.warn("表单绑定失败: {}", fieldErrors);

        String firstError = fieldErrors.values().iterator().hasNext()
                ? fieldErrors.values().iterator().next()
                : "表单数据绑定失败";

        return Result.fail(ResultCode.PARAM_INVALID.getCode(), firstError);
    }

    /**
     * 参数校验异常处理 - JSR303 单个参数校验
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<?> handleConstraintViolationException(ConstraintViolationException ex) {
        List<String> errorMessages = new ArrayList<>();

        ex.getConstraintViolations().forEach(violation -> {
            // 获取参数名（去掉方法名前缀）
            String propertyPath = violation.getPropertyPath().toString();
            String paramName = propertyPath.substring(propertyPath.lastIndexOf('.') + 1);
            String message = violation.getMessage();

            errorMessages.add(paramName + ": " + message);
        });

        log.warn("参数约束校验失败: {}", errorMessages);

        String errorMsg = String.join("; ", errorMessages);
        return Result.fail(ResultCode.PARAM_INVALID.getCode(),
                errorMessages.isEmpty() ? "参数校验失败" : errorMessages.get(0));
    }

    /**
     * 缺少请求参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
        String paramName = ex.getParameterName();
        String paramType = ex.getParameterType();
        String errorMsg = String.format("缺少必需的请求参数: %s (%s)", paramName, paramType);

        log.warn("缺少请求参数: paramName={}, paramType={}", paramName, paramType);

        return Result.fail(ResultCode.PARAM_MISSING.getCode(), errorMsg);
    }

    /**
     * 请求参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        String paramName = ex.getName();
        String paramValue = ex.getValue() != null ? ex.getValue().toString() : "null";
        String expectedType = ex.getRequiredType() != null ? ex.getRequiredType().getSimpleName() : "unknown";

        String errorMsg = String.format("参数类型错误: %s 的值 '%s' 无法转换为 %s 类型",
                paramName, paramValue, expectedType);

        log.warn("参数类型不匹配: paramName={}, value={}, expectedType={}",
                paramName, paramValue, expectedType);

        return Result.fail(ResultCode.PARAM_INVALID.getCode(), errorMsg);
    }

    /**
     * HTTP消息不可读异常（通常是JSON格式错误）
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        String errorMsg = "请求数据格式错误";

        // 尝试获取更具体的错误信息
        Throwable cause = ex.getCause();
        if (cause instanceof JsonParseException) {
            errorMsg = "JSON 格式错误，请检查请求数据";
        } else if (cause instanceof JsonMappingException) {
            JsonMappingException jsonEx = (JsonMappingException) cause;
            if (jsonEx.getPath() != null && !jsonEx.getPath().isEmpty()) {
                String fieldName = jsonEx.getPath().get(0).getFieldName();
                errorMsg = String.format("字段 '%s' 数据格式错误", fieldName);
            } else {
                errorMsg = "数据映射错误，请检查请求参数";
            }
        }

        log.warn("HTTP消息读取失败: {}", ex.getMessage());

        return Result.fail(ResultCode.PARAM_INVALID.getCode(), errorMsg);
    }

    /**
     * HTTP请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        String method = ex.getMethod();
        String[] supportedMethods = ex.getSupportedMethods();

        String errorMsg = String.format("不支持 %s 请求方法", method);
        if (supportedMethods != null && supportedMethods.length > 0) {
            errorMsg += "，支持的方法: " + Arrays.toString(supportedMethods);
        }

        log.warn("HTTP请求方法不支持: method={}, supportedMethods={}", method, supportedMethods);

        return Result.fail(ResultCode.METHOD_NOT_ALLOWED.getCode(), errorMsg);
    }

    /**
     * 媒体类型不支持异常
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<?> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
        MediaType contentType = ex.getContentType();
        List<MediaType> supportedTypes = ex.getSupportedMediaTypes();

        String errorMsg = "不支持的媒体类型";
        if (contentType != null) {
            errorMsg = String.format("不支持的媒体类型: %s", contentType);
        }
        if (!supportedTypes.isEmpty()) {
            errorMsg += "，支持的类型: " + supportedTypes;
        }

        log.warn("媒体类型不支持: contentType={}, supportedTypes={}", contentType, supportedTypes);

        return Result.fail(ResultCode.UNSUPPORTED_MEDIA_TYPE.getCode(), errorMsg);
    }

    /**
     * 非法参数异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleIllegalArgumentException(IllegalArgumentException ex) {
        String message = ex.getMessage();
        log.warn("非法参数异常: {}", message);

        return Result.fail(ResultCode.PARAM_INVALID.getCode(),
                StringUtils.hasText(message) ? message : "参数值不合法");
    }

    /**
     * 空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<?> handleNullPointerException(NullPointerException ex) {
        log.error("空指针异常", ex);
        return Result.fail(ResultCode.SERVER_ERROR.getCode(), "系统内部错误");
    }

    /**
     * 运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result<?> handleRuntimeException(RuntimeException ex) {
        log.error("运行时异常: {}", ex.getMessage(), ex);
        return Result.fail(ResultCode.SERVER_ERROR.getCode(), "系统内部错误");
    }

    /**
     * 通用异常处理
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception ex) {
        log.error("未知异常: {}", ex.getMessage(), ex);
        return Result.fail(ResultCode.SERVER_ERROR.getCode(), "系统异常，请联系管理员");
    }
}
